/*
 * Copyright 1999-2007 Alibaba.com All right reserved. This software is the confidential and proprietary information of
 * Alibaba.com ("Confidential Information"). You shall not disclose such Confidential Information and shall use it only
 * in accordance with the terms of the license agreement you entered into with Alibaba.com.
 */
package com.alibaba.kylin.biz.esite.bo;

import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Map.Entry;

import javax.imageio.ImageIO;

import org.apache.commons.fileupload.FileItem;
import org.apache.oro.text.regex.MalformedPatternException;
import org.apache.oro.text.regex.MatchResult;
import org.apache.oro.text.regex.Pattern;
import org.apache.oro.text.regex.PatternMatcher;
import org.apache.oro.text.regex.PatternMatcherInput;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
import org.json.JSONException;

import com.alibaba.china.biz.common.bo.FileUploadException;
import com.alibaba.china.biz.common.bo.UploadBO;
import com.alibaba.common.lang.StringUtil;
import com.alibaba.common.logging.Logger;
import com.alibaba.common.logging.LoggerFactory;
import com.alibaba.kylin.biz.esite.constant.EsiteConstants;
import com.alibaba.kylin.biz.esite.constant.ImageType;
import com.alibaba.kylin.biz.esite.util.EsiteUtil;
import com.alibaba.kylin.biz.esite.util.SimpleJsonUtil;
import com.alibaba.kylin.biz.esite.vo.FlashIndex;
import com.alibaba.kylin.biz.esite.vo.LogoImage;
import com.alibaba.trustpass.biz.dal.daointerface.BasesiteImageTrashDAO;
import com.alibaba.trustpass.biz.dal.dataobject.BasesiteConfigDO;
import com.alibaba.trustpass.biz.dal.dataobject.BasesiteOverallStyleDO;

/**
 * 负责basesite图片，flash的上传，复制逻辑
 * 
 * @author <a href="mailto:yihan.zhaoyh@alibaba-inc.com">Zhao Yihan</a>
 */
public class ImageUploadBO {

    private static final Logger   log                  = LoggerFactory.getLogger(ImageUploadBO.class);

    private static final String   IMG_UPLOAD_DIR       = "mysiteUpload";
    private static final String   FLASH_UPLOAD_DIR     = "flashUpload";
    private static final String   CSS_FILE_UPLOAD_DIR  = "cssUpload";
    private static final String   IMAGE_PATTERN_STR    = "url\\(([^)]*)\\)";
    private static Pattern        IMAGE_PATTERN;
    private static final char     FILE_NAME_CONNECTOR  = '_';
    private static final char     FILE_PATH_SEPARATOR  = '/';
    private static final String   IMAGE_URL_IDENTIFIER = "mysite";
    private static final int      IMAGE_URL_OFFSET     = IMAGE_URL_IDENTIFIER.length() + 1;

    static {
        try {
            IMAGE_PATTERN = new Perl5Compiler().compile(IMAGE_PATTERN_STR);
        } catch (MalformedPatternException e) {
            log.fatal(IMAGE_PATTERN_STR + " cannot be compiled", e);
        }
    }

    private UploadBO              uploadBO;
    private BasesiteImageTrashDAO basesiteImageTrashDAO;

    /**
     * 取得一个图片文件的高度，若获取失败，则返回默认高度
     * 
     * @param fileItem
     * @return
     */
    public int getImageHeight(FileItem fileItem, int defaultHeight) {
        if (fileItem == null) return 0;
        try {
            BufferedImage img = ImageIO.read(fileItem.getInputStream());
            return img.getHeight();
        } catch (Exception e) {
            // 忽略错误，让页面以默认高度显示
            if (log.isDebugEnabled()) {
                log.debug(e);
            }
            return defaultHeight;
        }
    }

    /**
     * 跟据固定高宽的值重新算图片的高度
     * 
     * @param fileItem
     * @param fixedHeight
     * @param fixedWidth
     * @return
     */
    public int getSuitableHeight(FileItem fileItem, int fixedHeight, int fixedWidth) {
        if (fileItem == null) return fixedHeight;
        try {
            BufferedImage img = ImageIO.read(fileItem.getInputStream());
            return caculateHeight(img.getHeight(), img.getWidth(), fixedHeight, fixedWidth);

        } catch (Exception e) {
            // 忽略错误，让页面以默认高度显示
            if (log.isDebugEnabled()) {
                log.debug(e);
            }
            return fixedHeight;
        }
    }

    /**
     * 把文件标志为待删除
     * 
     * @param memberId 会员ID
     * @param dels 需要删除的文件类型和url的map
     * @param refs 作为参考的文件类型和url的map(更新时用,传入更新后的值)
     */
    public void markFilesToDelete(String memberId, Map<ImageType, String> dels, Map<ImageType, String> refs) {
        if (StringUtil.isEmpty(memberId) || dels == null) {
            return;
        }

        StringBuilder toBeDeleted = new StringBuilder(128);

        for (Entry<ImageType, String> elem : dels.entrySet()) {
            ImageType type = elem.getKey();
            String path = elem.getValue();

            if (refs == null || refs.isEmpty() || (path != null && !path.equals(refs.get(type)))) {
                toBeDeleted.append(path).append(',');
            }
        }

        if (toBeDeleted.length() > 0) {
            String images = toBeDeleted.substring(0, toBeDeleted.length() - 1);
            try {
                basesiteImageTrashDAO.addImageToBeDeleted(memberId, images);
            } catch (Exception e) {
                log.error("Fail adding images to trash: [" + memberId + "], images: " + images);
            }
        }
    }

    /**
     * 上传文件
     * 
     * @param type 图片文件类型
     * @param memberId 会员ID
     * @param fileItem 待上传文件对象
     * @param i 会员上传的第i个图片或flash文件
     * @return 上传后的文件名,如果上传失败,返回null
     */
    public String uploadFile(ImageType type, String memberId, FileItem fileItem, int i) {
        if (type == null || fileItem == null || StringUtil.isEmpty(memberId)) {
            return null;
        }

        String dirKey = (ImageType.FLASH == type ? FLASH_UPLOAD_DIR : IMG_UPLOAD_DIR);

        try {
            String fileNameNoExt = generateFileName(type, memberId, i);
            String filePath = uploadBO.uploadFile(dirKey, fileItem, fileNameNoExt);
            if (filePath != null) {
                try {
                    basesiteImageTrashDAO.addImageToBeDeleted(memberId, filePath);
                } catch (Exception e) {
                    log.error("Fail adding images to trash: [" + memberId + "], images: " + filePath);
                }
            }
            return filePath;
        } catch (FileUploadException e) {
            log.error("Upload temp file error. ", e);
            return null;
        }

    }

    /**
     * 从风格对象中解析出图片地址
     * 
     * @param style
     * @return
     */
    public Map<ImageType, String> extractImagesFromStyle(BasesiteOverallStyleDO style) {
        Map<ImageType, String> images = new HashMap<ImageType, String>();

        if (style == null || StringUtil.isEmpty(style.getStyleContent())) {
            return images;
        }

        PatternMatcher matcher = new Perl5Matcher();
        PatternMatcherInput input = new PatternMatcherInput(style.getStyleContent());
        while (IMAGE_PATTERN != null && matcher.contains(input, IMAGE_PATTERN)) {
            MatchResult result = matcher.getMatch();
            String url = result.group(1);
            ImageType type = getImageTypeFromUrl(url);
            if (type != null) {
                images.put(type, getRelativeFilePathFromUrl(url));
            }
        }

        return images;

    }
    
    
    /**
     * 从风格对象中解析出炫彩滚动图片地址 <br>
     * edit by panlq at 2009.07.13
     * @param style
     * @return 
     */
    public List<String> extractGlareUrlsFromStyle(BasesiteOverallStyleDO style) {
        List<String> images = new ArrayList<String>();

        // 判断是否为空
        if (style == null || StringUtil.isEmpty(style.getGlareUrls())) {
            return images;
        }

        String[] urls = style.getGlareUrls().replaceAll(",{2,}", ",").split(",");
        
        // 循环将url放入
        for (String url : urls) {
            if (StringUtil.isNotEmpty(url)) {
                images.add(getRelativeFilePathFromUrl(url));
            }
        }

        return images;

    }
    

    /**
     * 从配置对象中提取图片地址
     * 
     * @param config
     * @return
     */
    public Map<ImageType, String> extractImagesFromConfig(BasesiteConfigDO config) {
        Map<ImageType, String> images = new HashMap<ImageType, String>();

        if (config == null) {
            return images;
        }

        if (!StringUtil.isEmpty(config.getLogoPic())) {
            LogoImage logoImage = null;
            try {
                logoImage = new LogoImage(config.getLogoPic());
            } catch (JSONException e) {
                EsiteUtil.log4JSONException(config.getMemberId(), "LogoImage", config.getLogoPic(), e);
            }
            if (logoImage != null) {
                images.put(ImageType.LOGO, getRelativeFilePathFromUrl(logoImage.getUrl()));
            }
        }
        if (!StringUtil.isEmpty(config.getFlashIndex())) {
            FlashIndex flashIndex = null;
            try {
                flashIndex = new FlashIndex(config.getFlashIndex());
            } catch (JSONException e) {
                EsiteUtil.log4JSONException(config.getMemberId(), "FlashIndex", config.getFlashIndex(), e);
            }
            if (flashIndex != null) {
                images.put(ImageType.FLASH, getRelativeFilePathFromUrl(flashIndex.getFilePath()));
            }
        }
        if (!StringUtil.isEmpty(config.getStyleFilePath())) {
            images.put(ImageType.CSS_FILE, config.getStyleFilePath());
        }

        return images;
    }

    /**
     * 由相对路径生成绝对路径
     * 
     * @param relativePath
     * @return
     */
    public String getAbsoluteFilePath(String relativePath) {
        ImageType type = getImageTypeFromUrl(relativePath);
        String dirKey = null;
        if (type == null) {
            return null;
        } else if (ImageType.CSS_FILE == type) {
            dirKey = CSS_FILE_UPLOAD_DIR;
        } else if (ImageType.FLASH == type) {
            dirKey = FLASH_UPLOAD_DIR;
        } else {
            dirKey = IMG_UPLOAD_DIR;
        }
        return uploadBO.getUploadDirAbsPath(dirKey) + relativePath;
    }

    /**
     * 批量删除文件
     * 
     * @param absolutePaths
     */
    public void deleteFiles(List<String> absolutePaths) {
        if (absolutePaths == null || absolutePaths.isEmpty()) {
            return;
        }
        uploadBO.deleteFiles(absolutePaths.toArray(new String[] {}));
    }

    /**
     * 根据正式应用的全局配置风格，输出一个CSS文件。
     * 
     * @param overallStyleId 已上网的basesite_overall_style的记录的id号
     * @param memberId 会员id
     * @param styleContent JSON数组格式的配置风格
     * @return CSS文件的相对路径。
     * @throws JSONException 解析JSON格式的字符串失败
     * @throws FileUploadException 操作文件失败
     */
    public String saveOverallStyle2File(int overallStyleId, String memberId, String styleContent) throws JSONException {
        if (overallStyleId <= 0 || StringUtil.isBlank(styleContent)) {
            return null;
        }

        // 构造文件名：12/34/1234_memberid_timestamp.css
        StringBuilder fileName = new StringBuilder();
        fileName.append(uploadBO.splitPath(String.valueOf(overallStyleId)));
        fileName.append(EsiteConstants.CSS_FILE_PATH_SEPARATOR);
        fileName.append(overallStyleId);
        fileName.append(EsiteConstants.CSS_UNDERSCORE);
        fileName.append(memberId);
        fileName.append(EsiteConstants.CSS_UNDERSCORE);
        fileName.append(System.currentTimeMillis());
        fileName.append(EsiteConstants.CSS_POINT);
        fileName.append(EsiteConstants.CSS_FILE_EXT);

        // 文件基准路径
        File basePath = uploadBO.getUploadDir(CSS_FILE_UPLOAD_DIR);
        File cssFile = new File(basePath, fileName.toString());
        // css文件的目录不存在，则新建目录
        if (!cssFile.getParentFile().exists()) {
            if (!cssFile.getParentFile().mkdirs()) {
                log.error("Failed to create subdirectory for file. " + cssFile);
                return null;
            }
        }

        // css文件不存在则新建文件
        if (!cssFile.exists()) {
            try {
                if (!cssFile.createNewFile()) {
                    log.error("Failed to create new file. " + cssFile);
                    return null;
                }
            } catch (IOException e) {
                log.error("Failed to create new file. " + cssFile);
                return null;
            }
        }

        // 一行一行写文件
        List<String> cssStrings = SimpleJsonUtil.styleContent2CSS(styleContent);
        if (cssStrings != null && cssStrings.size() > 0) {
            FileWriter cssFileWriter;
            try {
                cssFileWriter = new FileWriter(cssFile);
                BufferedWriter bufferedWriter = new BufferedWriter(cssFileWriter);
                for (String cssString : cssStrings) {
                    bufferedWriter.write(cssString);
                    bufferedWriter.newLine();
                }
                bufferedWriter.flush();
                bufferedWriter.close();
                cssFileWriter.close();
            } catch (IOException e) {
                log.error("Failed to write file. " + cssFile);
                return null;
            }

        }

        return fileName.toString();
    }

    protected ImageType getImageTypeFromUrl(String url) {
        if (StringUtil.isEmpty(url)) {
            return null;
        }
        if (url.endsWith(EsiteConstants.CSS_FILE_EXT)) {
            return ImageType.CSS_FILE;
        }
        int beginIndex = url.lastIndexOf(FILE_PATH_SEPARATOR);
        if (beginIndex < 0) {
            return null;
        }
        String fileName = url.substring(beginIndex);
        int endIndex = fileName.indexOf(FILE_NAME_CONNECTOR);
        if (endIndex < 0) {
            return null;
        }
        String prefix = fileName.substring(1, endIndex);
        
        
        return ImageType.getImageTypeByPrefix(prefix);
    }

    public String getRelativeFilePathFromUrl(String url) {
        if (StringUtil.isEmpty(url)) {
            return url;
        }
        int begin = url.indexOf(IMAGE_URL_IDENTIFIER);
        if (begin == -1) {
            return url;
        } else {
            return url.substring(begin + IMAGE_URL_OFFSET);
        }
    }

    private int caculateHeight(int height, int width, int fixedHeight, int fixedWidth) {
        if (height <= fixedHeight && width <= fixedWidth) {
            return height;
        }
        if ((double) height / fixedHeight > (double) width / fixedWidth) {
            return fixedHeight;
        } else {
            return height * fixedWidth / width;
        }
    }

    /**
     * <p>
     * 生成统一的不带后缀的文件名.正式文件格式为'12/34/56/pre_memberid_timestamp'. <br>
     * 其中'12/34/56'为随机生成的百万以内的整数按两位分隔而成.'pre_'为前缀
     * <p>
     * 例: athena2006的正式logo图片名为'11/34/l_athena2006_100700231987'
     * 
     * @param type 图片文件类型
     * @param memberId 会员ID
     * @param i 会员上传第i个图片或flash文件
     * @return 不带后缀的文件名
     * @throws FileUploadException 如果type或memberId没有
     */
    private String generateFileName(ImageType type, String memberId,int i) throws FileUploadException {
        if (type == null || StringUtil.isEmpty(memberId)) {
            throw new FileUploadException("Image type and the identifying memberId could not be null");
        }

        StringBuilder builder = new StringBuilder(64);
        builder.append(getRandomNumberPath(3, 100, FILE_PATH_SEPARATOR));
        builder.append(type.getPrefix()).append(i>0?String.valueOf(i):"");
        builder.append(FILE_NAME_CONNECTOR).append(memberId).append(FILE_NAME_CONNECTOR);
        builder.append(System.currentTimeMillis());

        return builder.toString();
    }

    private String getRandomNumberPath(int depth, int width, char seperator) {
        if (depth < 1 || width < 1) {
            return "";
        }
        StringBuilder sb = new StringBuilder(16);
        Random rand = new Random();
        for (int i = 0; i < depth; i++) {
            sb.append(rand.nextInt(width)).append(seperator);
        }
        return sb.toString();
    }

    public void setUploadBO(UploadBO uploadBO) {
        this.uploadBO = uploadBO;
    }

    public void setBasesiteImageTrashDAO(BasesiteImageTrashDAO basesiteImageTrashDAO) {
        this.basesiteImageTrashDAO = basesiteImageTrashDAO;
    }
}
